package cn.jasonone.jfx.manager;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jasonone.jfx.annotations.BindModel;
import cn.jasonone.jfx.annotations.BindProperty;
import cn.jasonone.jfx.annotations.BindPropertys;
import cn.jasonone.jfx.bind.DefaultBindProperty;
import cn.jasonone.jfx.context.BindPropertyUtil;
import cn.jasonone.jfx.context.JFXContextHolder;
import cn.jasonone.jfx.factory.BeanFactory;
import cn.jasonone.jfx.factory.BindPropertyFactory;
import cn.jasonone.jfx.factory.PropertyFactory;
import cn.jasonone.jfx.mananger.BindManager;
import javafx.beans.property.Property;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Control;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author xujie
 * @version 1.0
 * @date 2021/12/23 10:07
 */
@Slf4j
@Component("bindManager")
public class JfxBindManager implements BindManager {
    @Resource
    private BindPropertyFactory bindPropertyFactory;
    @Resource
    private PropertyFactory propertyFactory;
    @Resource
    private ApplicationContext applicationContext;

    @Override
    public void bind(Parent parent, Object controller) {
        Assert.notNull(controller, "控制器不能为null");
        Field[] fields = ReflectUtil.getFields(controller.getClass(), field ->
                AnnotationUtil.hasAnnotation(field, BindModel.class)
                        || AnnotationUtil.hasAnnotation(field, BindPropertys.class)
                        || AnnotationUtil.hasAnnotation(field, BindProperty.class)
        );
        for (Field field : fields) {
            if (AnnotationUtil.hasAnnotation(field, BindModel.class)) {
                BindModel bindModel = AnnotationUtils.getAnnotation(field, BindModel.class);
                Map<String, String> attributes = new HashMap<>();
                //region 处理控件绑定属性名称映射关系
                BindProperty[] bindProperties = bindModel.value();
                if (bindProperties != null) {
                    for (BindProperty bindProperty : bindProperties) {
                        String fxmlId = bindProperty.fxmlId();
                        String name = bindProperty.attributeName();
                        if (StrUtil.isAllNotBlank(fxmlId, name)) {
                            attributes.put(fxmlId, name);
                        }
                    }
                }
                //endregion


                Field[] modelFields = ReflectUtil.getFields(field.getType());
                Object value = ReflectUtil.getFieldValue(controller, field);
                if (value == null) {
                    value = newInstance(bindModel.beanFactoryType(), field);
                    ReflectUtil.setFieldValue(controller, field, value);
                }
                if (modelFields != null) {
                    for (Field modelField : modelFields) {
                        String fxmlId = modelField.getName();
                        String attributeName = attributes.get(fxmlId);
                        Node node = JFXContextHolder.lookup(parent, "#" + fxmlId);
                        Assert.notNull(node, "绑定的节点不存在:{}", fxmlId);
                        Assert.isTrue(node instanceof Control, "绑定的节点不是一个控件:{}", fxmlId);
                        Control control = (Control) node;
                        Object fieldValue = ReflectUtil.getFieldValue(value, modelField);
                        //region 创建绑定对象
                        if (fieldValue == null) {
                            fieldValue = newInstance(null, modelField);
                            ReflectUtil.setFieldValue(value, modelField, fieldValue);
                        }
                        //endregion

                        if (StrUtil.isBlank(attributeName)) {
                            attributeName = getAttributeName(control.getClass(), attributeName);
                        }
                        // 绑定属性
                        BindPropertyUtil.bind(control, attributeName, value, modelField.getName());
                    }
                }
            } else if (AnnotationUtil.hasAnnotation(field, BindPropertys.class) || AnnotationUtil.hasAnnotation(field, BindProperty.class)) {
                Set<BindProperty> bindPropertys = AnnotationUtils.getRepeatableAnnotations(field, BindProperty.class);
                for (BindProperty bindProperty : bindPropertys) {
                    bindModel(parent, controller, field, bindProperty);
                }
            }
        }
    }

    private Object newInstance(Class<? extends BeanFactory> beanFactoryType, Field field) {
        BeanFactory beanFactory = null;
        Object fieldValue = null;
        // 检查是否配置了BeanFactory工厂
        if (beanFactoryType != null && !beanFactoryType.getName().equals(BeanFactory.class.getName())) {
            // 如果配置了BeanFactory工厂,则从IOC容器中获取
            beanFactory = applicationContext.getBean(beanFactoryType);
            Assert.notNull(beanFactory, "未在IOC容器中找到[{}]", beanFactoryType);
        }
        if (Property.class.isAssignableFrom(field.getType())) {
            try {
                return propertyFactory.getInstance(field.getType());
            } catch (Exception e) {
                log.error("属性实例化失败:" + field, e);
            }
        }
        if (beanFactory == null) {
            // 如果BeanFactory工厂未空,则获取默认工厂创建
            beanFactory = new BeanFactory() {
            };
        }
        try {
            // 创建属性对象
            fieldValue = beanFactory.getBean(field.getType());
        } catch (Exception e) {
            log.error("属性实例化失败:" + field, e);
        }
        return fieldValue;
    }

    private void bindModel(Parent parent, Object model, Field field, BindProperty bindProperty) {
        String id = StrUtil.blankToDefault(bindProperty.fxmlId(), field.getName());
        Node node = JFXContextHolder.lookup(parent, "#" + id);
        Assert.notNull(node, "绑定的节点不存在:{}", id);
        Assert.isTrue(node instanceof Control, "绑定的节点不是一个控件:{}", id);
        Control control = (Control) node;
        String attributeName = getAttributeName(control.getClass(), bindProperty.attributeName());
        Object fieldValue = ReflectUtil.getFieldValue(model, field);
        if (Property.class.isAssignableFrom(field.getType())) {
            if (fieldValue == null) {
                Class<? extends Property> type = (Class<? extends Property>) field.getType();
                fieldValue = propertyFactory.getInstance(type);
            }
            ReflectUtil.setFieldValue(model, field, fieldValue);
            BindPropertyUtil.bind(control, attributeName, (Property) fieldValue);
        } else {
            if (fieldValue == null) {
                BindPropertyUtil.bind(control, attributeName, model, field.getName());
            }
        }
    }

    private String getAttributeName(Class<? extends Control> controlClass, String attributeName) {
        if (StrUtil.isBlank(attributeName)) {
            DefaultBindProperty defaultBindProperty = bindPropertyFactory.getDefaultBindProperty(controlClass);
            attributeName = defaultBindProperty.getAttribute();
        }
        return attributeName;
    }
}
